{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "import qkit\n",
    "qkit.start()\n",
    "\n",
    "from qkit.measure import samples_class as sample # Sample class\n",
    "from qkit.measure.timedomain import sequence_library as sl # Sequence library for standard experiments\n",
    "from qkit.measure.timedomain import pulse_sequence as ps # Pulse sequence class to build sequences of your own\n",
    "from qkit.measure.timedomain import VirtualAWG as VirtAWG # virtual awg for managing your sequence objects"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Initializing test sample"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "testsample = sample.Sample()\n",
    "testsample.readout_tone_length = 200e-9 # length of the readout tone\n",
    "testsample.clock = 1e9 # sample rate of your physical awg/pulse generator\n",
    "testsample.tpi = 100e-9 # duration of a pi-pulse\n",
    "testsample.tpi2 = 50e-9 # duration of a pi/2-pulse\n",
    "testsample.iq_frequency = 20e6 # iq_frequency for iq mixing (set to 0 for homodyne measurements)\n",
    "\n",
    "#testsample.awg = my_awg #<- qkit instrument (your actual awg)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Building sequences\n",
    "Sequences are python objects, encoding the experiment you want to run on your pulse generator.\n",
    "They are built from pulses (another type of object), wait times, and the readout."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### The pulse object\n",
    "Pulse objects are initialized with:\n",
    "\n",
    "mypulse = ps.Pulse(length, pulse-shape, name, amplitude, phase, iq_frequency, iq_dc_offset, iq_angle)\n",
    "<br>\n",
    "**length** is the pulse length in seconds <br>\n",
    "**pulse-shape** is the shape of the pulse. Currently rect (square pulse, this is the default) and gaussian shapes are implemented. To use gaussian shape, write *shape = ps.ShapeLib.gauss*<it><br>\n",
    "**name** is the name you want to give your pulse. This is not mandatory and only used for plotting.<br>\n",
    "**amplitude**: relative amplitude of your pulse.<br>\n",
    "**phase**: relative phase of your pulse **in degree**.<br>\n",
    "**iq_frequency**: If iq_frequency is 0, homodyne mixing is used.<br>\n",
    "**iq_dc_offset, iq_angle** are currently not in use, but will be used in the near future to enable a calibration of the mixers."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#example:\n",
    "pi = ps.Pulse(50e-9, name = \"pi-pulse\", shape = ps.ShapeLib.gauss, iq_frequency=50e6)\n",
    "#this creates a 50ns gaussian pulse with name \"pi-pulse\" at an iq_frequency of 50MHz."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Building sequences from pulses:\n",
    "Sequences are built from pulse objects in an intuitive way: You just start adding pulses to your sequence, which are then appended. In the example below, a simple T1 measurement sequence is built, using our recently defined pi-pulse."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "my_sequence = ps.PulseSequence(testsample) # create sequence object\n",
    "my_sequence.add(pi) # add pi pulse, as defined in the example above\n",
    "my_sequence.add_wait(lambda t: t) # add a variable wait time with length t\n",
    "my_sequence.add_readout() # add the readout\n",
    "my_sequence.plot() # show SCHEMATIC plot of the pulse sequence"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As you can see, wait times can be added with the *add_wait* command. In this case the wait time is given by a **lambda function**. This enable the implementation of variable time steps. This can also be used to add pulses with variable lengths.<br>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Of course, there are also pre-built sequences for standard experiments, which you can find in the sequence_library class (here imported as sl).\n",
    "##### Currently, this includes sequences for **Rabi**, **T1**, **Ramsey**, **spin-echo** and **spin-locking experiments**."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "spinecho = sl.spinecho(testsample, n_pi = 2) # spinecho with 2 pi-pulses\n",
    "spinecho.plot()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Single channel virtual AWG"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "vawg = VirtAWG.VirtualAWG(testsample) # by default, the virtual awg is initialized with a single channel\n",
    "time = np.arange(0, 500e-9, 50e-9) # time t for the sequence\n",
    "vawg.set_sequence(my_sequence, time) # set_sequence deletes all previously stored sequences in a channel\n",
    "vawg.add_sequence(spinecho, time * 2) # add_sequence appends the next sequence to the sequences stored in the channel\n",
    "# Note, this enables you to run multiple experiments, such as T1-measurement and spin-echo in parallel!#\n",
    "vawg.plot()\n",
    "\n",
    "# In the plot, the time starts at 0 together with the readout. \n",
    "# The position of the readout is also used as a phase reference for all pulses."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Please note, that this plot always displays the amplitude of your signal (not I or Q).\n",
    "##### We refrained from displaying the pulses with their iq_frequency to prevent confusion.\n",
    "##### Similarly, it is **not** necessary to initialize the virtual awg with a channel for each I and Q. Whether two physical channels are needed to generate the desired output is determined automatically by the load script (see below).\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# If you do not want the experiments to run consecutively, but to interleave them instead:\n",
    "vawg.set_interleave(True)\n",
    "# This also works for more than 2 sequences.\n",
    "vawg.plot()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If you are satisfied with the results, load the sequences onto your physical device with:\n",
    "<br>vawg.load() <br>\n",
    "*Currently, this is only enabled for the tabor awg.*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Multi-channel virtual AWG\n",
    "This feature enables the user to run multiple sequences on different channels at the same time. <br>\n",
    "The readout of each sequence is used to synchronize the channels (i.e. it is expected that the readout happens simultaneously)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "vawg = VirtAWG.VirtualAWG(testsample, channels = 2) #Initialize with two channels channel (number is arbitrary)\n",
    "vawg.set_sequence(my_sequence, time, channel = 1) # set my_sequence (T1 measurement) on channel 1\n",
    "vawg.set_sequence(spinecho, time, channel = 2) # set spinecho on channel 2\n",
    "vawg.plot()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 2",
   "language": "python",
   "name": "python2"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.14"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
